"""
股票技术分析工具模块

该模块提供以下功能函数：
1. StockTechnicalAnalyzer - 股票技术分析主类
   - __init__: 初始化API客户端
     [输入] api_key: 可选API密钥
     [输出] 无
   - _init_client: 创建OpenAI客户端实例
     [输入] 无
     [输出] OpenAI客户端对象
   - get_stock_data: 获取股票历史数据(90天日线+30周周线)
     [输入] symbol: 股票代码
     [输出] 包含日线和周线数据的markdown数据
   - read_prompt_template: 从文件读取提示词模板
     [输入] file_path: 模板文件路径
     [输出] 模板字符串
   - generate_analysis_prompt: 生成技术分析提示词
     [输入] data_json: 股票数据字典
     [输出] 格式化后的提示词字符串
   - analyze_stock: 执行股票技术分析
     [输入] symbol: 股票代码
     [输出] 分析结果字符串

2. CustomJSONEncoder - 自定义JSON编码器
   - default: 处理日期和Pandas Timestamp类型的序列化
     [输入] obj: 需要序列化的对象
     [输出] 序列化后的字符串
"""

import os
import datetime
import pandas as pd
import akshare as ak
import json
import logging
from openai import OpenAI

# 设置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(name)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger(__name__)

# 常量定义
PROMPT_TEMPLATE_PATH = "wxcloudrun/prompt/stock_analysis_prompt_template.txt"  # 提示词模板文件路径
DEFAULT_PROMPT_TEMPLATE = """
数据输入：90天日线+30周线, 包括一下以markdown整理的数据
{stock_data} 当前分析时间戳 {current_time} 
需要对于数据做出短线分析，包括几个角度
1、常规的短线分析手段
2、波浪理论分析
3、缠论分析

最后的输出的框架
1、核心结论[50字]
2、经典道氏分析,先给出总结[30字以内], 然后给出技术面特征概述和核心指标信号, 需要关注的部分加粗
3、波浪理论分析, 包括当前阶段[浪型]的判断, 然后给出接下来可能的走势, 如果有多种可能尽量给出概率
4、缠论分析
5、给出最后的综合结论以及分析预测
6、给出操作建议
最后标明以及结论有AI推理产出,不作为投资建议和参考

输出规范：
1、用markdown格式输出, 调整排版让内容更加美观
2. 用**加粗**标记核心结论，🔴标红≤3处重大风险  
3. 每个模块用图标引导（🔍📊🚨👑🌍💡）
4. 每段之间适当的留空行和分割线, 让阅读更轻松一些
"""

class CustomJSONEncoder(json.JSONEncoder):
    """自定义JSON编码器，处理日期和Pandas Timestamp类型"""
    def default(self, obj):
        if isinstance(obj, (datetime.date, datetime.datetime)):
            return obj.isoformat()
        elif isinstance(obj, pd.Timestamp):
            return obj.strftime('%Y-%m-%d')
        return super().default(obj)

class StockTechnicalAnalyzer:
    def __init__(self, api_key=None, prompt_template_path=PROMPT_TEMPLATE_PATH):
        """
        初始化股票技术分析器
        
        [输入]
        api_key: 可选API密钥
        prompt_template_path: 提示词模板文件路径
        
        [输出] 无
        """
        self.api_key = api_key or os.getenv("DASHSCOPE_API_KEY")
        self.prompt_template_path = prompt_template_path
        self.client = self._init_client()
        #self.prompt_template = self.read_prompt_template()
        self.prompt_template = DEFAULT_PROMPT_TEMPLATE
    def _init_client(self):
        """
        初始化OpenAI客户端
        
        [输入] 无
        [输出] OpenAI客户端实例
        """
        if not self.api_key:
            raise ValueError("API key is required. Set DASHSCOPE_API_KEY environment variable.")
        return OpenAI(
            api_key=self.api_key,
            base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
        )
    
    def read_prompt_template(self):
        """
        从文件读取提示词模板
        
        [输入] 无
        [输出] 模板字符串
        """
        try:
            if os.path.exists(self.prompt_template_path):
                with open(self.prompt_template_path, 'r', encoding='utf-8') as file:
                    return file.read()
            else:
                logger.warning(f"提示词模板文件 {self.prompt_template_path} 不存在，使用默认模板")
                return DEFAULT_PROMPT_TEMPLATE
        except Exception as e:
            logger.error(f"读取提示词模板失败: {str(e)}，使用默认模板")
            return DEFAULT_PROMPT_TEMPLATE

    def get_stock_data(self, symbol):
        """
        获取股票历史数据(90天日线+30周周线)
        
        [输入] symbol: 股票代码
        [输出] 包含日线和周线数据的markdown数据
        """
        today = datetime.date.today()
        daily_days = 90 * 2
        weekly_weeks = 30 * 2
        start_date_daily = (today - datetime.timedelta(days=daily_days))
        start_date_weekly = (today - datetime.timedelta(weeks=weekly_weeks))
        end_date = today

        try:
            # 获取日线数据
            daily_data = ak.stock_zh_a_hist(
                symbol=symbol,
                period="daily",
                start_date=start_date_daily.strftime("%Y%m%d"),
                end_date=end_date.strftime("%Y%m%d"),
                adjust="qfq"
            )

            if len(daily_data) < 90:
                logger.warning(f"获取的日线数据不足90条，仅有{len(daily_data)}条")
            daily_data = daily_data.tail(90)  # 取最后90条数据

            # 获取周线数据
            weekly_data = ak.stock_zh_a_hist(
                symbol=symbol,
                period="weekly",
                start_date=start_date_weekly.strftime("%Y%m%d"),
                end_date=end_date.strftime("%Y%m%d"),
                adjust="qfq"
            )

            if len(weekly_data) < 30:
                logger.warning(f"获取的周线数据不足30条，仅有{len(weekly_data)}条")
            weekly_data = weekly_data.tail(30)  # 取最后30条数据

            # 处理日线数据
            daily_list = []
            for _, row in daily_data[::-1].iterrows():  # 逆序排列，最早的日期在前
                daily_list.append({
                    "date": row['日期'].strftime('%Y-%m-%d') if isinstance(row['日期'], pd.Timestamp) else row['日期'],
                    "open": round(row['开盘'], 2),
                    "close": round(row['收盘'], 2),
                    "high": round(row['最高'], 2),
                    "low": round(row['最低'], 2),
                    "volume": int(row['成交量']),
                    "turnover": round(row['成交额'], 2),
                    "amplitude": round(row['振幅'], 2),
                    "pct_change": round(row['涨跌幅'], 2),
                    "turnover_rate": round(row['换手率'], 2)
                })

            # 处理周线数据
            weekly_list = []
            for _, row in weekly_data[::-1].iterrows():  # 逆序排列，最早的日期在前
                weekly_list.append({
                    "week_start": row['日期'].strftime('%Y-%m-%d') if isinstance(row['日期'], pd.Timestamp) else row['日期'],
                    "open": round(row['开盘'], 2),
                    "close": round(row['收盘'], 2),
                    "high": round(row['最高'], 2),
                    "low": round(row['最低'], 2),
                    "volume": int(row['成交量']),
                    "turnover": round(row['成交额'], 2),
                    "amplitude": round(row['振幅'], 2),
                    "pct_change": round(row['涨跌幅'], 2),
                    "turnover_rate": round(row['换手率'], 2)
                })
            final_res = "股票代码:" + symbol + "\n"
            final_res += "日数据:\n" + pd.DataFrame(daily_list).to_markdown()
            final_res += "周数据:\n" + pd.DataFrame(weekly_list).to_markdown()
            final_res += "\n"
            return final_res
        except Exception as e:
            logger.error(f"获取股票数据失败: {str(e)}")
            raise RuntimeError(f"获取股票数据失败: {str(e)}")

    def generate_analysis_prompt(self, stock_data):
        # 序列化股票数据
        """
        生成技术分析提示词
        
        [输入] data_json: 股票数据字典
        [输出] 格式化后的提示词字符串
        """
        current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        try:
            # 替换模板中的占位符
            instruction = self.prompt_template.format(stock_data=stock_data, current_time=current_time)
        except KeyError:
            logger.warning("模板中的占位符格式错误，使用默认模板")
            instruction = DEFAULT_PROMPT_TEMPLATE.format(stock_data=stock_data, current_time=current_time)
        return instruction

    def analyze_stock(self, symbol):
        """
        执行股票技术分析
        
        [输入] symbol: 股票代码
        [输出] 分析结果字符串
        """
        try:
            # 获取股票数据
            stock_data = self.get_stock_data(symbol)
            # 生成提示词
            prompt = self.generate_analysis_prompt(stock_data)
            # 调用大模型进行分析
            response = self.client.chat.completions.create(
                model="qwen-plus",
                messages=[
                    {
                        "role": "system",
                        "content": "你是专业股票分析师，精通技术分析和量化策略，"
                                   "特别擅长量价分析、波浪理论和缠论结构分析。"
                                   "请严格按照用户要求的三维度框架进行分析，"
                                   "并给出具体的可操作策略。"
                    },
                    {"role": "user", "content": prompt}
                ],
                temperature=0.3,
                max_tokens=2000
            )
            return response.choices[0].message.content
        except Exception as e:
            logger.error(f"股票分析失败: {str(e)}")
            return f"分析失败: {str(e)}"

if __name__ == "__main__":
    # 测试配置
    TEST_SYMBOL = "002594"
    API_KEY = os.getenv("DASHSCOPE_API_KEY")  # 从环境变量获取API密钥
    
    if not API_KEY:
        print("警告：未设置DASHSCOPE_API_KEY环境变量，部分功能可能受限")
    
    print(f"开始测试股票技术分析模块，股票代码: {TEST_SYMBOL}")
    
    try:
        # 创建分析器实例
        analyzer = StockTechnicalAnalyzer(api_key=API_KEY)
        
        # 测试数据获取
        print("\n=== 测试数据获取 ===")
        data = analyzer.get_stock_data(TEST_SYMBOL)
#print(data)

        # 测试分析功能
        print("\n=== 测试技术分析 ===")
        print("注意：此步骤将调用大模型API，可能需要一些时间...")
        analysis_result = analyzer.analyze_stock(TEST_SYMBOL)
        
        print("\n分析结果:")
        print("="*80)
        print(analysis_result)
        print("="*80)
        """
        # 测试结果验证
        if "失败" in analysis_result or "错误" in analysis_result:
            print("\n测试结果: ❌ 分析失败")
        else:
            print("\n测试结果: ✅ 分析成功")
            # 检查关键内容
            key_phrases = ["MACD", "RSI", "支撑位", "目标价", "止损价"]
            found_phrases = [phrase for phrase in key_phrases if phrase in analysis_result]
            print(f"结果包含关键指标: {found_phrases}")
           """ 
    except Exception as e:
        print(f"\n测试失败: {str(e)}")
        import traceback
        traceback.print_exc()
